#pragma once

#include "typedefs.h"
#include "controls.h"

namespace img { class bitmap; }

namespace outline {

/// fills interior of B-spline with true, other pixels are reset to false state,
/// param bmp must be preresized
template <typename R>
void render(controls<R> const & ctrls, img::bitmap & bmp);

template <typename R>
struct poly_bezier_builder_params {
	R sharpness_penalty;
	/// initially when simplify selects where to insert control points it builds curves with low quality
	bool low;
	unsigned low_max_iterations;
	R low_stop_mean_discrepancy;
	R low_oversample_factor;
	/// at finish curves are rebuilded with high quality
	unsigned hi_max_iterations;
	R hi_stop_mean_discrepancy;
	R hi_oversample_factor;
	bool fit_poly_bezier_as_a_whole;
};

template <typename T, typename R = typename type_traits<T>::real_type>
class poly_bezier_builder_base : public typedefs<T, R>
{
public:
	struct estimation_type {
		real_type d; //discrepancy
		real_point_type d1, d2; //estimated control points (relative to nearby junction points)

		estimation_type() { }
		estimation_type(real_type dd, real_point_type const & dd1, real_point_type const & dd2) : 
			d(dd), d1(dd1), d2(dd2) { }
		void null() { d = real_type(-1); }
		bool is_null() const { return d < 0; }
	};
	typedef std::vector<estimation_type> estimation_vector_type;
	typedef std::vector<estimation_vector_type> estimation_matrix_type;
public:
	poly_bezier_builder_base();
	~poly_bezier_builder_base();

	bool empty() const { return nodes_.empty(); }
	/// Output from the fuctions is a vector of nodes, that should be interpretd as follows:
	/// start point, control point 1, control point 2, common point, control point 1, control point 2...
	/// (The size of the vector is one more than three times the number of pieces, 
	/// because each Bezier curve requires two control points and an endpoint, and the initial curve requires an additional starting point.)
	/// \return discrepancy of found best segmentation (including penalty), negative result means that nothing found for given length_ and max_junctions parameter
	/// second parameter returns positions of controls before refitting (or empty vector if no estimations were made)
	R collect_best(controls<R> * rendered_ctrls, controls<R> * estimated_ctrls = 0,
		real_point_vector_type const * attractors = 0, real_vector_type const * tractions = 0);
	/// returns penalty for the discontinuity of curve derivative at a junction with
	/// given relative position of control points
	static R sharpness_penalty(real_type max,
		real_point_type const & d1, real_point_type const & d2);
	///selects best junction nodes
	virtual R select_best(index_vector_type & ivec) = 0;
	virtual poly_bezier_builder_params<R> const & get_params() const = 0;
	virtual estimation_type const & get_estimation(uint begin, uint len, uint piece_num) const = 0;
	virtual void get_junction_types(index_vector_type const & ivec, std::vector<junc_type> & junc) const = 0; //vector is preresized
protected:
	void reset_nodes(nodes_type const & polyline);
	void clear_nodes();
protected:
	struct step_type {
		uint m; //first junction node's index
		real_type d; //total discrepancy

		step_type() { }
		step_type(unsigned int mm, real_type dd) : m(mm), d(dd) { }
		void null() { d = real_type(-1); }
		bool is_null() const { return d < 0; }
	};
	typedef std::vector<step_type> step_vector_type;
	typedef std::vector<step_vector_type> step_matrix_type;
protected:
	uint size_;
	nodes_type nodes_; ///< size_*2 elements, periodity condition: nodes_[i] = nodes_[i+size_]
};

// inlines

template <typename T, typename R>
inline R poly_bezier_builder_base<T, R>::sharpness_penalty(real_type max,
	real_point_type const & d1, real_point_type const & d2)
{
	if (max == 0)
		return 0;
	if (scalar_product(d1, d2) >= 0)
		return max;
	return max * square(outer_product(d1, d2)) / (square(d1) * square(d2));
}

} //namespace outline
